/******************************************************
 * @file disp_manager.c
 * @author Destiny 
 * @brief 显示设备管理
 * 
 * @version 0.1
 * @date 2024-07-18
 *****************************************************/
#include <config.h>
#include <disp_manager.h>
#include <string.h>
#include <stdlib.h>



static PT_DispOpr g_ptDispOprHead;     /*显示设备链表头*/
static PT_DispOpr g_ptDefaultDispOpr;  /*默认设备*/
static PT_VideoMem g_ptVideoMemHead;   /*显示缓冲区链表头*/
/*
 * @description		    : 向框架注册显示设备
 * @param - ptDispOpr 	: 需要注册的显示设备结构体指针
 * @return 			    : 0 成功;其他 失败
 */
int RegisterDispOpr(PT_DispOpr ptDispOpr)
{
	PT_DispOpr ptTmp;

	if (!g_ptDispOprHead)
	{
		g_ptDispOprHead   = ptDispOpr;
		ptDispOpr->ptNext = NULL;
	}
	else
	{
		ptTmp = g_ptDispOprHead;
		while (ptTmp->ptNext)
		{
			ptTmp = ptTmp->ptNext;
		}
		ptTmp->ptNext	  = ptDispOpr;
		ptDispOpr->ptNext = NULL;
	}

	return 0;
}

/*
 * @description		    : 打印所有显示设备名称
 * @param - ptDebugOpr 	: 无
 * @return 			    : 无
 */
void ShowDispOpr(void)
{
	int i = 0;
	PT_DispOpr ptTmp = g_ptDispOprHead;

	while (ptTmp)
	{
		printf("%02d %s\n", i++, ptTmp->name);
		ptTmp = ptTmp->ptNext;
	}
}
/*
 * @description		    : 依据名称得到对应显示设备结构体指针
 * @param - pcName   	: 显示设备名称
 * @return 			    : 成功 显示设备结构体指针;失败 NULL
 */
PT_DispOpr GetDispOpr(char *pcName)
{
	PT_DispOpr ptTmp = g_ptDispOprHead;
	
	while (ptTmp)
	{
		if (strcmp(ptTmp->name, pcName) == 0)
		{
			return ptTmp;
		}
		ptTmp = ptTmp->ptNext;
	}
	return NULL;
}

/*
 * @description		    : 选择并初始化显示设备
 * @param - pcName   	: 显示设备名称
 * @return 			    : 无
 */
void SelectAndInitDefaultDispDev(char *name)
{
	g_ptDefaultDispOpr = GetDispOpr(name);
	if (g_ptDefaultDispOpr)
	{
		g_ptDefaultDispOpr->DeviceInit();
		g_ptDefaultDispOpr->CleanScreen(0);
	}
}

/*
 * @description		    : 得到默认的显示设备结构体指针
 * @param 			   	: 无
 * @return 			    : 成功 显示设备结构体指针;失败 NULL
 */
PT_DispOpr GetDefaultDispDev(void)
{
	return g_ptDefaultDispOpr;
}
/*
 * @description		    : 得到显示设备分辨率
 * @param - piXres   	: 指针，指向X分辨率
 * @param - piYres   	: 指针，指向Y分辨率
 * @param - piBpp   	: 指针，指向像素位数
 * @return 			    : 0 成功;其他 失败
 */
int GetDispResolution(int *piXres, int *piYres, int *piBpp)
{
	if (g_ptDefaultDispOpr)
	{
		*piXres = g_ptDefaultDispOpr->iXres;
		*piYres = g_ptDefaultDispOpr->iYres;
		*piBpp  = g_ptDefaultDispOpr->iBpp;
		return 0;
	}
	else
	{
		return -1;
	}
}
/*
 * @description		    : 分配显示缓冲区
 * @param - iNum   		: 需分配的显示缓冲区的个数
 * @return 			    : 0 成功;其他 失败
 */
int AllocVideoMem(int iNum)
{
	int i;

	int iXres = 0;
	int iYres = 0;
	int iBpp  = 0;

	int iVMSize;     /*设备显示缓冲区大小*/
	int iLineBytes;  /*设备显示缓冲区一行所占字节数*/

	PT_VideoMem ptNew;

	/*得到设备显示缓冲区大小*/
	GetDispResolution(&iXres, &iYres, &iBpp);    
	iVMSize = iXres * iYres * iBpp / 8;
	iLineBytes = iXres * iBpp / 8;

	/* 为显示缓冲区描述结构体分配内存 */
	ptNew = malloc(sizeof(T_VideoMem));    
	if (ptNew == NULL)
	{
		return -1;
	}
	/*第一个显示缓冲区结构体指向显示设备显示区*/
	ptNew->tPixelDatas.aucPixelDatas = g_ptDefaultDispOpr->pucDispMem;
	ptNew->iID = 0;
	ptNew->bDevFrameBuffer = 1;        
	ptNew->eVideoMemState  = VMS_FREE;
	ptNew->ePicState	   = PS_BLANK;
	ptNew->tPixelDatas.iWidth  = iXres;
	ptNew->tPixelDatas.iHeight = iYres;
	ptNew->tPixelDatas.iBpp    = iBpp;
	ptNew->tPixelDatas.iLineBytes  = iLineBytes;
	ptNew->tPixelDatas.iTotalBytes = iVMSize;
	if (iNum != 0)   /*需要额外分配显示缓冲区*/
	{
		/* 如果下面要分配用于缓存的VideoMem, 
		 * 把设备本身framebuffer对应的VideoMem状态设置为VMS_USED_FOR_CUR,
		 * 表示这个VideoMem不会被作为缓存分配出去
		 */
		ptNew->eVideoMemState = VMS_USED_FOR_CUR;
	}
	/*当前显示缓冲区设置为链表头*/
	ptNew->ptNext = g_ptVideoMemHead;
	g_ptVideoMemHead = ptNew;

	/*分配显示缓冲区*/
	for (i = 0; i < iNum; i++)
	{
		ptNew = malloc(sizeof(T_VideoMem) + iVMSize);   /*一次性分配结构体和显示缓冲区*/
		if (ptNew == NULL)
		{
			return -1;
		}
		ptNew->tPixelDatas.aucPixelDatas = (uint8_t *)(ptNew + 1);  /*显示缓冲区首地址紧接其描述结构体*/
		ptNew->iID = 0;
		ptNew->bDevFrameBuffer = 0;
		ptNew->eVideoMemState = VMS_FREE;
		ptNew->ePicState      = PS_BLANK;
		ptNew->tPixelDatas.iWidth  = iXres;
		ptNew->tPixelDatas.iHeight = iYres;
		ptNew->tPixelDatas.iBpp    = iBpp;
		ptNew->tPixelDatas.iLineBytes = iLineBytes;
		ptNew->tPixelDatas.iTotalBytes = iVMSize;
		ptNew->ptNext = g_ptVideoMemHead;
		g_ptVideoMemHead = ptNew;
	}
	return 0;
}
/*
 * @description		    : 获得一块显示缓冲区
 * @param - iID   		: ID值,先尝试在众多VideoMem中找到ID值相同的
 * @param - bCur   		: 1 必须返回一个VideoMem，0 不是必需的
 * @return 			    : 显示缓冲区描述结构体指针
 */
PT_VideoMem GetVideoMem(int iID, int bCur)
{
	PT_VideoMem ptTmp = g_ptVideoMemHead;
	
	/* 取出空闲的、ID相同的videomem*/
	while (ptTmp)
	{
		if ((ptTmp->eVideoMemState == VMS_FREE) && (ptTmp->iID == iID))
		{
			ptTmp->eVideoMemState = bCur ? VMS_USED_FOR_CUR : VMS_USED_FOR_PREPARE;
			return ptTmp;
		}
		ptTmp = ptTmp->ptNext;
	}

	/*取出空闲的、数据为空*/
	ptTmp = g_ptVideoMemHead;
	while (ptTmp)
	{
		if ((ptTmp->eVideoMemState == VMS_FREE) && (ptTmp->ePicState == PS_BLANK))
		{
			ptTmp->iID = iID;
			ptTmp->eVideoMemState = bCur ? VMS_USED_FOR_CUR : VMS_USED_FOR_PREPARE;
			return ptTmp;
		}
		ptTmp = ptTmp->ptNext;
	}	
	
	/*取出空闲的*/
	ptTmp = g_ptVideoMemHead;
	while (ptTmp)
	{
		if (ptTmp->eVideoMemState == VMS_FREE)
		{
			ptTmp->iID = iID;
			ptTmp->ePicState = PS_BLANK;
			ptTmp->eVideoMemState = bCur ? VMS_USED_FOR_CUR : VMS_USED_FOR_PREPARE;
			return ptTmp;
		}
		ptTmp = ptTmp->ptNext;
	}

    /*直接取出g_ptVideoMemHead所指向的缓冲区*/
    if (bCur)
    {
    	ptTmp = g_ptVideoMemHead;
    	ptTmp->iID = iID;
    	ptTmp->ePicState = PS_BLANK;
    	ptTmp->eVideoMemState = bCur ? VMS_USED_FOR_CUR : VMS_USED_FOR_PREPARE;
    	return ptTmp;
    }
	return NULL;
}
/*
 * @description		    : 释放缓冲区
 * @param - ptVideoMem  : 需释放的显示缓冲区的结构体指针
 * @return 			    : 无
 */
void PutVideoMem(PT_VideoMem ptVideoMem)
{
	ptVideoMem->eVideoMemState = VMS_FREE;  
    if (ptVideoMem->iID == -1)
    {
        ptVideoMem->ePicState = PS_BLANK;  
    }
}
/*
 * @description		    : 得到当前设备显示缓冲区地址
 * @param - 			: 无
 * @return 			    : 成功 设备显示缓冲区的结构体指针;失败 NULL
 */
PT_VideoMem GetDevVideoMem(void)
{
	PT_VideoMem ptTmp = g_ptVideoMemHead;
	
	while (ptTmp)
	{
		if (ptTmp->bDevFrameBuffer)
		{
			return ptTmp;
		}
		ptTmp = ptTmp->ptNext;
	}
	return NULL;
}
/*
 * @description		    : 将显示缓冲区设置为指定颜色
 * @param - ptVideoMem  : 显示缓冲区描述结构体指针
 * @param - dwColor   	: 需设置的颜色
 * @return 			    : 无
 */
void ClearVideoMem(PT_VideoMem ptVideoMem, uint32_t dwColor)
{
	uint8_t *pucVM;
	uint16_t *pwVM16bpp;
	uint32_t *pdwVM32bpp;
	uint16_t wColor16bpp; 
	int iRed;
	int iGreen;
	int iBlue;
	int i = 0;

	pucVM	   = ptVideoMem->tPixelDatas.aucPixelDatas;    /*显示缓冲区首地址*/
	pwVM16bpp  = (uint16_t *)pucVM;                  
	pdwVM32bpp = (uint32_t *)pucVM;

	switch (ptVideoMem->tPixelDatas.iBpp)
	{
		case 8:
		{
			memset(pucVM, dwColor, ptVideoMem->tPixelDatas.iTotalBytes);
			break;
		}
		case 16:
		{
			iRed   = (dwColor >> (16+3)) & 0x1f;
			iGreen = (dwColor >> (8+2)) & 0x3f;
			iBlue  = (dwColor >> 3) & 0x1f;
			wColor16bpp = (iRed << 11) | (iGreen << 5) | iBlue;
			while (i < ptVideoMem->tPixelDatas.iTotalBytes)
			{
				*pwVM16bpp	= wColor16bpp;
				pwVM16bpp++;
				i += 2;
			}
			break;
		}
		case 32:
		{
			while (i < ptVideoMem->tPixelDatas.iTotalBytes)
			{
				*pdwVM32bpp = dwColor;
				pdwVM32bpp++;
				i += 4;
			}
			break;
		}
		default :
		{
			DBG_PRINTF(DLOG_LVL_ERROR,DLOG_TAG,"can't support %d bpp\n", ptVideoMem->tPixelDatas.iBpp);
			return;
		}
	}

}
/*
 * @description		    : 把ptPageLayout所指区域设置为指定颜色
 * @param - ptVideoMem  : 显示缓冲区描述结构体指针
 * @param - ptPageLayout: 页面布局描述结构体指针
 * @param - dwColor   	: 需设置的颜色
 * @return 			    : 无
 */
void ClearVideoMemRegion(PT_VideoMem ptVideoMem, PT_PageLayout ptPageLayout, uint32_t dwColor)
{
	uint8_t *pucVM;
	uint16_t *pwVM16bpp;
	uint32_t *pdwVM32bpp;
	uint16_t wColor16bpp; 
	int iRed;
	int iGreen;
	int iBlue;
	int iX;
	int iY;
    int iLineBytesClear;
    int i;

	pucVM	   = ptVideoMem->tPixelDatas.aucPixelDatas +  ptPageLayout->iTopLeftY * ptVideoMem->tPixelDatas.iLineBytes +  ptPageLayout->iTopLeftX * ptVideoMem->tPixelDatas.iBpp / 8;
	pwVM16bpp  = (uint16_t *)pucVM;
	pdwVM32bpp = (uint32_t *)pucVM;

    iLineBytesClear = ( ptPageLayout->iBotRightX -  ptPageLayout->iTopLeftX + 1) * ptVideoMem->tPixelDatas.iBpp / 8;

	switch (ptVideoMem->tPixelDatas.iBpp)
	{
		case 8:
		{
            for (iY =  ptPageLayout->iTopLeftY; iY <=  ptPageLayout->iBotRightY; iY++)
            {
    			memset(pucVM, dwColor, iLineBytesClear);
                pucVM += ptVideoMem->tPixelDatas.iLineBytes;
            }
			break;
		}
		case 16:
		{
			
			iRed   = (dwColor >> (16+3)) & 0x1f;
			iGreen = (dwColor >> (8+2)) & 0x3f;
			iBlue  = (dwColor >> 3) & 0x1f;
			wColor16bpp = (iRed << 11) | (iGreen << 5) | iBlue;
            for (iY =  ptPageLayout->iTopLeftY; iY <=  ptPageLayout->iBotRightY; iY++)
            {
                i = 0;
                for (iX =  ptPageLayout->iTopLeftX; iX <=  ptPageLayout->iBotRightX; iX++)
    			{
    				pwVM16bpp[i++]	= wColor16bpp;
    			}
                pwVM16bpp = (uint16_t *)((uint32_t)pwVM16bpp + ptVideoMem->tPixelDatas.iLineBytes);
            }
			break;
		}
		case 32:
		{
            for (iY =  ptPageLayout->iTopLeftY; iY <=  ptPageLayout->iBotRightY; iY++)
            {
                i = 0;
                for (iX =  ptPageLayout->iTopLeftX; iX <=  ptPageLayout->iBotRightX; iX++)
    			{
    				pdwVM32bpp[i++]	= dwColor;
    			}
                pdwVM32bpp = (uint32_t *)((uint32_t)pdwVM32bpp + ptVideoMem->tPixelDatas.iLineBytes);
            }
			break;
		}
		default :
		{
			DBG_PRINTF(DLOG_LVL_ERROR,DLOG_TAG,"can't support %d bpp\n", ptVideoMem->tPixelDatas.iBpp);
			return;
		}
	}

}

/*设备初始化*/
int DisplayInit(void)
{
	int iError;
	
	iError = FBInit();

	return iError;
}

